﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.IO;
using Bling.Core;
using Bling.Angles;
using Bling.DSL;
using Bling.Util;
using Bling.WPF;
using linq = Microsoft.Linq.Expressions;
using Bling.Matrices;
using Bling.Linq;
using System.Reflection;

namespace Bling.WPF3D {
  using Bling.Graphics;
  /// <summary>
  ///     Provides a rendering surface for 3-D visual content.
  /// </summary>
  public class Viewport3DBl : FrameworkElementBl<Viewport3D, Viewport3DBl> {
    public Viewport3DBl() : base(new Constant<Viewport3D>(new Viewport3D())) { }
    public Viewport3DBl(Expr<Viewport3D> Provider) : base(Provider) { }
    public Viewport3DBl(PanelBl canvas) : base(canvas, new Viewport3D()) {}
    public static implicit operator Viewport3DBl(Expr<Viewport3D> target) { return new Viewport3DBl((target)); }
    public static implicit operator Viewport3DBl(Viewport3D target) { return (new Constant<Viewport3D>(target)); }
    /// <summary>
    /// Configures this view port with a flat/bird eye perspective where its visual consumes its entire allocated space.
    /// </summary>
    public Double2Bl FlatPerspective {
      set {
        var MeshSize = value;
        this.ClipToBounds = false;
        this.IsHitTestVisible = false;
        this.EdgeMode = System.Windows.Media.EdgeMode.Aliased;
        this.BitmapScalingMode = System.Windows.Media.BitmapScalingMode.NearestNeighbor;
        this.Children.AddOne = new ModelVisual3DBl() {
          Content = new SpotLightBl() {
            Color = Colors.White,
            Position = new Double3Bl(MeshSize.X, MeshSize.Y * 0.25, 3),
            Direction = new Double3Bl(0, 0, -1),
            InnerConeAngle = 70d.Degrees(),
            OuterConeAngle = 100d.Degrees(),
          },
        };
        this.Camera = new OrthographicCameraBl() {
          Position = new Double3Bl(MeshSize.X * 0.5, MeshSize.Y * 0.5, 1.0),
          Direction = {
            Look = new Double3Bl(0, 0, -1),
            Up = new Double3Bl(0, 1, 0),
          },
          Width = MeshSize.X, PlaneDistance = { Far = 5, Near = 0.1 },
        };
      }
    }

    /// <summary>
    ///     Gets or sets a camera object that projects the 3-D contents of the System.Windows.Controls.Viewport3D to
    ///     the 2-D surface of the System.Windows.Controls.Viewport3D. This is a dependency
    ///     property.
    /// </summary>
    public CameraBl Camera {
      get { return Provider.Property<Viewport3D,Camera>(Viewport3D.CameraProperty); }
      set { Camera.Bind = value; }
    }
    /// <summary>
    /// Gets a collection of the System.Windows.Media.Media3D.Visual3D children of
    ///  the System.Windows.Controls.Viewport3D. This is a dependency property.
    /// </summary>
    public CollectionBl<Visual3D, Visual3DBl, Visual3DCollection> Children { get { return Provider.Property<Viewport3D,Visual3DCollection>(Viewport3D.ChildrenProperty); } }
  }
  public abstract class Model3DBl<T, BRAND> : Brand<T, BRAND>, IExtends<BRAND,Model3DBl>
    where T : Model3D
    where BRAND : Model3DBl<T, BRAND> {
    public Model3DBl(Expr<T> Underlying) : base(Underlying) { }
    public Model3DBl() : base() { }

    public static implicit operator Model3DBl(Model3DBl<T, BRAND> mod) { return Model3DBl.UpCast(mod); }
  }
  /// <summary>
  ///     Abstract class that provides functionality for 3-D models.
  /// </summary>
  public class Model3DBl : Model3DBl<Model3D, Model3DBl> {
    public Model3DBl(Expr<Model3D> Underlying) : base(Underlying) { }
    public Model3DBl() : base() { }
    public static implicit operator Model3DBl(Expr<Model3D> target) { return new Model3DBl((target)); }
    public static implicit operator Model3DBl(Model3D target) { return (new Constant<Model3D>(target)); }
  }

  public class Model3DGroupBl : Model3DBl<Model3DGroup, Model3DGroupBl> {
    public Model3DGroupBl(Expr<Model3DGroup> Underlying) : base(Underlying) { }
    public Model3DGroupBl() : base(new Model3DGroup()) { }
    public CollectionBl<Model3D, Model3DBl, Model3DCollection> Children { get { return Provider.Property<Model3DGroup,Model3DCollection>(Model3DGroup.ChildrenProperty); } }
  }

  public class GeometryModel3DBl : Model3DBl<GeometryModel3D, GeometryModel3DBl> {
    public GeometryModel3DBl(Expr<GeometryModel3D> Underlying) : base(Underlying) { }
    public GeometryModel3DBl() : base(new GeometryModel3D()) { }
    public MaterialBl Material {
      get { return Provider.Property<GeometryModel3D,Material>(GeometryModel3D.MaterialProperty); }
      set { Material.Bind = value; }
    }
    public MaterialBl BackMaterial {
      get { return Provider.Property<GeometryModel3D, Material>(GeometryModel3D.BackMaterialProperty); }
      set { BackMaterial.Bind = value; }
    }
    public Geometry3DBl Geometry {
      get { return Provider.Property<GeometryModel3D,Geometry3D>(GeometryModel3D.GeometryProperty); }
      set { Geometry.Bind = value; }
    }
  }

  /// <summary>
  ///     System.Windows.Media.Media3D.Model3D object that represents lighting applied
  ///     to a 3-D scene.
  /// </summary>
  public abstract class LightBl<T, BRAND, GENERIC> : Model3DBl<T, BRAND>, IExtends<BRAND,LightBl>, ILight
    where T : Light
    where BRAND : LightBl<T, BRAND, GENERIC> where GENERIC : LightCl {
    public LightBl(Expr<T> Underlying) : base(Underlying) { }
    public LightBl() : base() { }
    /// <summary>
    ///     Gets or sets the color of the light.
    /// </summary>
    public ColorBl Color {
      get { return Provider.Property<T,Color>(Light.ColorProperty); }
      set { Color.Bind = value; }
    }
    public static implicit operator LightBl(LightBl<T, BRAND, GENERIC> light) {
      return LightBl.UpCast(light);
    }

    public static LightGroup operator +(LightBl<T, BRAND, GENERIC> OpA, ILight OpB) {
      return new LightGroup(OpA, OpB);
    }

    protected LightCl Translate(Light light) {
      if (light is SpotLight)        return new        SpotLightCl() { CopyLight = ((SpotLightBl)((SpotLight)light)) };
      if (light is DirectionalLight) return new DirectionalLightCl() { CopyLight = ((DirectionalLightBl)((DirectionalLight)light)) };
      if (light is AmbientLight)     return new AmbientLightCl() { CopyLight = ((AmbientLightBl)((AmbientLight)light)) };
      if (light is PointLight)       return new PointLightCl() { CopyLight = ((PointLightBl)((PointLight)light)) };
      throw new NotSupportedException();
    }
    public virtual Double3Bl DirectionToPosition(Double3Bl Position) { return Translate(CurrentValue).DirectionToPosition(Position); }
    public virtual DoubleBl UseAttenuation(Double3Bl Direction, DoubleBl Distance) { return Translate(CurrentValue).UseAttenuation(Direction, Distance); }
    //public virtual DoubleBl UseAttenuation(Double3Bl Direction) { return Translate(CurrentValue).UseAttenuation(Direction); }
  }
  /// <summary>
  ///     System.Windows.Media.Media3D.Model3D object that represents lighting applied
  ///     to a 3-D scene.
  /// </summary>
  public class LightBl : LightBl<Light, LightBl, LightCl> {
    public LightBl(Expr<Light> Underlying) : base(Underlying) { }
    public LightBl() : base() { }
    public static implicit operator LightBl(Expr<Light> target) { return new LightBl((target)); }
    public static implicit operator LightBl(Light target) { return (new Constant<Light>(target)); }
  }
  /// <summary>
  ///     Light object that projects its effect along a direction specified by a System.Windows.Media.Media3D.Vector3D.
  /// </summary>
  public class DirectionalLightBl : LightBl<DirectionalLight, DirectionalLightBl, DirectionalLightCl>, IDirectionalLight {
    public DirectionalLightBl(Expr<DirectionalLight> Underlying) : base(Underlying) { }
    public DirectionalLightBl() : this(new Constant<DirectionalLight>(new DirectionalLight())) { }
    public static implicit operator DirectionalLightBl(Expr<DirectionalLight> target) { return new DirectionalLightBl((target)); }
    public static implicit operator DirectionalLightBl(DirectionalLight target) { return (new Constant<DirectionalLight>(target)); }
    static DirectionalLightBl() { 
      //Vector3DArity.CheckInit();
    }
    /// <summary>
    ///     Represents the vector along which the light's effect will be seen on models
    ///     in a 3-D scene. This is a dependency property.
    ///     Vector3D along which the light projects, and which must have a non-zero magnitude.
    ///     The default value is (0,0,-1).
    /// </summary>
    public Double3Bl Direction {
      get { return Provider.Property<DirectionalLight,Vector3D>(DirectionalLight.DirectionProperty); }
      set { Direction.Bind = value; }
    }
    public override Double3Bl DirectionToPosition(Double3Bl Position) 
    { return new DirectionalLightCl() { CopyLight = this }.DirectionToPosition(Position); }
    public override DoubleBl UseAttenuation(Double3Bl Direction, DoubleBl Distance) 
    { return new DirectionalLightCl() { CopyLight = this }.UseAttenuation(Direction, Distance); }
    //public override DoubleBl UseAttenuation(Double3Bl Direction) { return new DirectionalLightCl() { CopyLight = this }.UseAttenuation(Direction); }
    
  }
  /// <summary>
  ///     Light object that applies light to objects uniformly, regardless of their
  ///    shape.
  /// </summary>
  public class AmbientLightBl : LightBl<AmbientLight, AmbientLightBl, AmbientLightCl>, IAmbientLight {
    public AmbientLightBl(Expr<AmbientLight> Underlying) : base(Underlying) { }
    public AmbientLightBl() : this(new Constant<AmbientLight>(new AmbientLight())) { }
    public static implicit operator AmbientLightBl(Expr<AmbientLight> target) { return new AmbientLightBl((target)); }
    public static implicit operator AmbientLightBl(AmbientLight target) { return (new Constant<AmbientLight>(target)); }

    public override Double3Bl DirectionToPosition(Double3Bl Position) { return new AmbientLightCl() { CopyLight = this }.DirectionToPosition(Position); }
    public override DoubleBl UseAttenuation(Double3Bl Direction, DoubleBl Distance) { return new AmbientLightCl() { CopyLight = this }.UseAttenuation(Direction, Distance); }
    //public override DoubleBl UseAttenuation(Double3Bl Direction) { return new AmbientLightCl() { CopyLight = this }.UseAttenuation(Direction); }
  }
  public abstract class PointLightBl<T, BRAND, GENERIC> : LightBl<T, BRAND, GENERIC>, IPointLight
    where T : PointLightBase
    where BRAND : PointLightBl<T, BRAND, GENERIC> where GENERIC : PointLightCl {
    public PointLightBl(Expr<T> Underlying) : base(Underlying) { }

    private class AttenuationSet : IAttenuationSet {
      internal Expr<T> Underlying;
      /// <summary>
      ///     Gets or sets a constant value by which the intensity of the light diminishes
      ///     over distance. This is a dependency property.
      /// </summary>
      public DoubleBl Constant {
        get { return Underlying.Property<T,double>(PointLightBase.ConstantAttenuationProperty); }
        set { Constant.Bind = value; }
      }
      /// <summary>
      ///     Gets or sets a value that specifies the linear diminution of the light's
      ///     intensity over distance. This is a dependency property.
      /// </summary>
      public DoubleBl Linear {
        get { return Underlying.Property<T,double>(PointLightBase.LinearAttenuationProperty); }
        set { Linear.Bind = value; }
      }
      /// <summary>
      ///     Gets or sets a value that specifies the diminution of the light's effect
      ///     over distance, calculated by a quadratic operation. This is a dependency
      ///     property.
      /// </summary>
      public DoubleBl Quadratic {
        get { return Underlying.Property<T,double>(PointLightBase.QuadraticAttenuationProperty); }
        set { Quadratic.Bind = value; }
      }
      public IAttenuationSet Bind {
        set {
          this.Linear = value.Linear; this.Quadratic = value.Quadratic;
          this.Constant = value.Constant;
        }
      }
      internal AttenuationSet() { }
    }
    /// <summary>
    /// Get or set values that specify the diminution of the light's effect
    /// over distance, calculated by constant, linear, and quadratic operations.
    /// likes:Attenuation = 1/constant + linear * distance + quadratic * distance * distance
    /// </summary>
    public IAttenuationSet Attenuation {
      get { return new AttenuationSet() { Underlying = Provider }; }
      set { Attenuation.Bind = (value); }
    }
    /// <summary>
    ///     Gets or sets a System.Windows.Media.Media3D.Point3D that specifies the light's
    ///     position in world space. This is a dependency property.
    /// </summary>
    public Double3Bl Position {
      get { return Provider.Property<T,Point3D>(PointLightBase.PositionProperty); }
      set { Position.Bind = value; }
    }
    static PointLightBl() {
      //Point3DArity.CheckInit();
    }

    /// <summary>
    ///     Gets or sets the distance beyond which the light has no effect. This is a
    ///     dependency property.
    /// </summary>
    public DoubleBl Range {
      get { return Provider.Property<T,double>(PointLightBase.RangeProperty); }
      set { Range.Bind = value; }
    }
  }

  /// <summary>
  ///     Represents a light source that has a specified position in space and projects
  ///     its light in all directions.
  /// </summary>
  public class PointLightBl : PointLightBl<PointLight, PointLightBl, PointLightCl> {
    public PointLightBl(Expr<PointLight> Underlying) : base(Underlying) { }
    public PointLightBl() : this(new Constant<PointLight>(new PointLight())) { }
    public static implicit operator PointLightBl(Expr<PointLight> target) { return new PointLightBl((target)); }
    public static implicit operator PointLightBl(PointLight target) { return (new Constant<PointLight>(target)); }
    public override Double3Bl DirectionToPosition(Double3Bl Position) { return new PointLightCl() { CopyLight = this }.DirectionToPosition(Position); }
    public override DoubleBl UseAttenuation(Double3Bl Direction, DoubleBl Distance) { return new PointLightCl() { CopyLight = this }.UseAttenuation(Direction, Distance); }
    //public override DoubleBl UseAttenuation(Double3Bl Direction) { return new PointLightCl() { CopyLight = this }.UseAttenuation(Direction); }
  }
  /// <summary>
  ///     Light object that projects its effect in a cone-shaped area along a specified
  ///     direction.
  /// </summary>
  public class SpotLightBl : PointLightBl<SpotLight, SpotLightBl, SpotLightCl>, ISpotLight {
    public SpotLightBl(Expr<SpotLight> Underlying) : base(Underlying) { }
    public SpotLightBl() : this(new Constant<SpotLight>(new SpotLight())) { }
    public static implicit operator SpotLightBl(Expr<SpotLight> target) { return new SpotLightBl((target)); }
    public static implicit operator SpotLightBl(SpotLight target) { return (new Constant<SpotLight>(target)); }
    static SpotLightBl() { 
      //Vector3DArity.CheckInit();  
    }
    /// <summary>
    ///     Gets or sets a System.Windows.Media.Media3D.Vector3D that specifies the direction
    ///     in which the System.Windows.Media.Media3D.SpotLight projects its light. This
    ///     is a dependency property. The default value is 0,0,-1.
    /// </summary>
    public Double3Bl Direction {
      get { return Provider.Property<SpotLight,Vector3D>(SpotLight.DirectionProperty); }
      set { Direction.Bind = value; }
    }
    /// <summary>
    ///     Gets or sets an angle that specifies the proportion of a System.Windows.Media.Media3D.SpotLight's
    ///     cone-shaped projection in which the light fully illuminates objects in the
    ///     scene. This is a dependency property. The default value is 180.
    /// </summary>
    public DegreeBl InnerConeAngle {
      get { return Provider.Property<SpotLight, double>(SpotLight.InnerConeAngleProperty).Bl().Degrees(); }
      set { InnerConeAngle.Bind = value; }
    }
    /// <summary>
    ///     Gets or sets an angle that specifies the proportion of a System.Windows.Media.Media3D.SpotLight's
    ///     cone-shaped projection outside which the light does not illuminate objects
    ///     in the scene. This is a dependency property. The default value is 90.
    /// </summary>
    public DegreeBl OuterConeAngle {
      get { return Provider.Property<SpotLight, double>(SpotLight.OuterConeAngleProperty).Bl().Degrees(); }
      set { OuterConeAngle.Bind = value; }
    }
    public override Double3Bl DirectionToPosition(Double3Bl Position) { return new SpotLightCl() { CopyLight = this }.DirectionToPosition(Position); }
    public override DoubleBl UseAttenuation(Double3Bl Direction, DoubleBl Distance) { return new SpotLightCl() { CopyLight = this }.UseAttenuation(Direction, Distance); }
    //public override DoubleBl UseAttenuation(Double3Bl Direction) { return new SpotLightCl() { CopyLight = this }.UseAttenuation(Direction); }
  }
  /// <summary>
  ///     Provides services and properties common to visual 3-D objects, including
  ///     hit-testing, coordinate transformation, and bounding box calculations.
  /// </summary>
  public abstract class Visual3DBl<T, BRAND> : RenderBl<T, BRAND>, IExtends<BRAND,Visual3DBl>
    where T : Visual3D
    where BRAND : Visual3DBl<T, BRAND> {
    public Visual3DBl(Expr<T> Underlying) : base(Underlying) { }
    public Visual3DBl() : base() { }
    public Visual3DBl(Viewport3DBl Viewport, T value) : base(value) {
      Viewport.Children.Add(value);
    }
    public static implicit operator Visual3DBl(Visual3DBl<T, BRAND> mat) {
      return Visual3DBl.UpCast(mat);
    }
    /// <summary>
    ///     The transformation to apply to the 3-D object. The default is the System.Windows.Media.Media3D.Transform3D.Identity
    ///     transformation.
    /// </summary>
    public Transform3DBl Transform {
      get { return Provider.Property<T,Transform3D>(Visual3D.TransformProperty); }
      set { Transform.Bind = value; }
    }
  }
  /// <summary>
  ///     Provides services and properties common to visual 3-D objects, including
  ///     hit-testing, coordinate transformation, and bounding box calculations.
  /// </summary>
  public class Visual3DBl : Visual3DBl<Visual3D, Visual3DBl> {
    public Visual3DBl(Expr<Visual3D> Underlying) : base(Underlying) { }
    public Visual3DBl() : base() { }
    public static implicit operator Visual3DBl(Expr<Visual3D> target) { return new Visual3DBl((target)); }
    public static implicit operator Visual3DBl(Visual3D target) { return (new Constant<Visual3D>(target)); }
  }
  public abstract class UIElement3DBl<T, BRAND> : Visual3DBl<T, BRAND>, IExtends<BRAND, UIElement3DBl> where T : UIElement3D where BRAND : UIElement3DBl<T,BRAND> {
    public UIElement3DBl(Expr<T> Underlying) : base(Underlying) { }
    public UIElement3DBl() : base() { }
    public UIElement3DBl(Viewport3DBl Viewport, T value) : base(Viewport, value) { }
    public static implicit operator UIElement3DBl(UIElement3DBl<T, BRAND> mat) {
      return UIElement3DBl.UpCast(mat);
    }
  }
  public class UIElement3DBl : UIElement3DBl<UIElement3D, UIElement3DBl> {
    public UIElement3DBl(Expr<UIElement3D> Underlying) : base(Underlying) { }
    public UIElement3DBl() : base() { }
  }

  /// <summary>
  ///     System.Windows.Media.Visual that contains 3-D models.
  /// </summary>
  public class ModelVisual3DBl : Visual3DBl<ModelVisual3D, ModelVisual3DBl> {
    public ModelVisual3DBl(Expr<ModelVisual3D> Underlying) : base(Underlying) { }
    public ModelVisual3DBl(Viewport3DBl Viewport) : base(Viewport, new ModelVisual3D()) { }
    public ModelVisual3DBl() : base(new Constant<ModelVisual3D>(new ModelVisual3D())) { }
    public static implicit operator ModelVisual3DBl(Expr<ModelVisual3D> target) { return new ModelVisual3DBl((target)); }
    public static implicit operator ModelVisual3DBl(ModelVisual3D target) { return (new Constant<ModelVisual3D>(target)); }
    /// <summary>
    ///     System.Windows.Media.Media3D.Transform3D set on the model. The default value
    ///     is System.Windows.Media.Media3D.MatrixTransform3D.
    /// </summary>
    public new Transform3DBl Transform {
      get { return Provider.Property<ModelVisual3D,Transform3D>(ModelVisual3D.TransformProperty); }
      set { Transform.Bind = value; }
    }
    /// <summary>
    /// System.Windows.Media.Media3D.Model3D that comprises the content of the 
    /// System.Windows.Media.Media3D.ModelVisual3D.
    /// </summary>
    public Model3DBl Content {
      get { return Provider.Property<ModelVisual3D,Model3D>(ModelVisual3D.ContentProperty); }
      set { Content.Bind = value; }
    }
  }

  /// <summary>
  ///     Renders the 2-D children within the specified 3-D viewport bounds.
  /// </summary>
  public class Viewport2DVisual3DBl : Visual3DBl<Viewport2DVisual3D, Viewport2DVisual3DBl> {
    public Viewport2DVisual3DBl(Expr<Viewport2DVisual3D> Underlying) : base(Underlying) { }
    public Viewport2DVisual3DBl() : base(new Constant<Viewport2DVisual3D>(new Viewport2DVisual3D())) { }
    public Viewport2DVisual3DBl(Viewport3DBl Viewport) : base(Viewport, new Viewport2DVisual3D()) { }
    public static implicit operator Viewport2DVisual3DBl(Expr<Viewport2DVisual3D> target) { return new Viewport2DVisual3DBl((target)); }
    public static implicit operator Viewport2DVisual3DBl(Viewport2DVisual3D target) { return (new Constant<Viewport2DVisual3D>(target)); }
    /// <summary>
    ///     The 3-D geometry for this System.Windows.Media.Media3D.Viewport2DVisual3D.
    /// </summary>
    public Geometry3DBl Geometry { get { return Provider.Property<Viewport2DVisual3D,Geometry3D>(Viewport2DVisual3D.GeometryProperty); } set { Geometry.Bind = value; } }
    /// <summary>
    ///     The material for the 3-D object.
    /// </summary>
    public MaterialBl Material { get { return Provider.Property<Viewport2DVisual3D,Material>(Viewport2DVisual3D.MaterialProperty); } set { Material.Bind = value; } }
    /// <summary>
    ///     The visual to be placed on the 3-D object.
    /// </summary>
    public VisualBl Visual { get { return Provider.Property<Viewport2DVisual3D,Visual>(Viewport2DVisual3D.VisualProperty); } set { Visual.Bind = value; } }
  }

  /// <summary>
  ///     Abstract base class for materials.
  /// </summary>
  public abstract class MaterialBl<T, BRAND> : Brand<T, BRAND>, IExtends<BRAND,MaterialBl>, IMaterial 
    where T : Material
    where BRAND : MaterialBl<T, BRAND> {
    public MaterialBl(Expr<T> Underlying) : base(Underlying) { }
    public MaterialBl() : base() { }
    public static implicit operator MaterialBl(MaterialBl<T, BRAND> mat) {
      return MaterialBl.UpCast(mat);
    }
    public abstract Lighting ApplyLights(LightGroup Light);

    /// <summary>
    /// XXX: no documentation found.
    /// </summary>
    public BoolBl IsVisualHostMaterial {
      get { return Provider.Property<T,bool>(Viewport2DVisual3D.IsVisualHostMaterialProperty); }
      set { IsVisualHostMaterial.Bind = value; }
    }
    //public abstract Lighting this[ILight Light] { get; }
  }
  /// <summary>
  ///     Abstract base class for materials.
  /// </summary>
  public class MaterialBl : MaterialBl<Material, MaterialBl> {
    public MaterialBl(Expr<Material> Underlying) : base(Underlying) { }
    public MaterialBl() : base() { }
    public static implicit operator MaterialBl(Expr<Material> target) { return new MaterialBl((target)); }
    public static implicit operator MaterialBl(Material target) { return (new Constant<Material>(target)); }
    public override Lighting ApplyLights(LightGroup Light) {
      // cannot tell 
      throw new NotImplementedException();
    }
  }
  /// <summary>
  ///     Allows the application of a 2-D brush, like a System.Windows.Media.SolidColorBrush
  ///     or System.Windows.Media.TileBrush, to a diffusely-lit 3-D model.
  /// </summary>
  public class DiffuseMaterialBl : MaterialBl<DiffuseMaterial, DiffuseMaterialBl>, IDiffuseMaterial {
    public DiffuseMaterialBl(Expr<DiffuseMaterial> Underlying) : base(Underlying) { }
    public DiffuseMaterialBl() : this(new Constant<DiffuseMaterial>(new DiffuseMaterial())) { }
    public static implicit operator DiffuseMaterialBl(Expr<DiffuseMaterial> target) { return new DiffuseMaterialBl((target)); }
    public static implicit operator DiffuseMaterialBl(DiffuseMaterial target) { return (new Constant<DiffuseMaterial>(target)); }
    /// <summary>
    ///     A knob, the color allowed to emit from the System.Windows.Media.Media3D.Material.
    ///     The default value is white (#FFFFFF). Since all colors make up white, all colors
    ///     are visible by default.
    /// </summary>
    public RgbBl Knob {
      get {
        ColorBl clr = Provider.Property<DiffuseMaterial,Color>(DiffuseMaterial.ColorProperty);
        return (RgbBl)clr;
      }
      set { Knob.Bind = value; }
    }
    /// <summary>
    ///     System.Windows.Media.Brush to be applied as a System.Windows.Media.Media3D.Material
    ///     to a 3-D model. This is a dependency property.
    /// </summary>
    public BrushBl Brush {
      get { return Provider.Property<DiffuseMaterial,Brush>(DiffuseMaterial.BrushProperty); } 
      set { Brush.Bind = value; } 
    }
    private class AmbientSet : IKnobish {
      internal DiffuseMaterialBl Outer;
      public RgbBl Knob {
        get {
          ColorBl clr = Outer.Provider.Property<DiffuseMaterial,Color>(DiffuseMaterial.AmbientColorProperty);
          return (RgbBl) clr; 
        }
        set { Knob.Bind = value; }
      }
      public void BindKnob(IKnobish Other) { this.Knob = Other.Knob; } 
    }
    public void BindKnob(IKnobish Other) { this.Knob = Other.Knob; }
    /// <summary>
    ///     The color allowed to emit from the ambient light of the material. The default value
    ///     is white (#FFFFFF).
    /// </summary>
    public IKnobish Ambient {
      get { return new AmbientSet() { Outer = this }; }
      set { this.Ambient.BindKnob(value); }
    }
    public override Lighting ApplyLights(LightGroup Light) {
      return new DiffuseMaterialCl() {
        Knob = Knob,
        Ambient = Ambient,
      }.ApplyLights(Light);
    }
  }
  /// <summary>
  ///     Allows a 2-D brush, like a System.Windows.Media.SolidColorBrush or System.Windows.Media.TileBrush,
  ///     to be applied to a specularly-lit 3-D model.
  /// </summary>
  public class SpecularMaterialBl : MaterialBl<SpecularMaterial, SpecularMaterialBl>, ISpecularMaterial {
    public SpecularMaterialBl(Expr<SpecularMaterial> Underlying) : base(Underlying) { }
    public SpecularMaterialBl() : this(new Constant<SpecularMaterial>(new SpecularMaterial())) { }
    public static implicit operator SpecularMaterialBl(Expr<SpecularMaterial> target) { return new SpecularMaterialBl((target)); }
    public static implicit operator SpecularMaterialBl(SpecularMaterial target) { return (new Constant<SpecularMaterial>(target)); }
    /// <summary>
    ///     Gets or sets a value that filters the color properties of the material applied
    ///     to the model. This is a dependency property.
    /// </summary>
    public RgbBl Knob { 
      get {
        ColorBl clr = Provider.Property<SpecularMaterial,Color>(SpecularMaterial.ColorProperty);
        return (RgbBl)clr;
      } 
      set { Knob.Bind = value; } 
    }
    public void BindKnob(IKnobish value) { Knob = value.Knob; }
    /// <summary>
    ///     Gets or sets the 2-D brush to apply to a specularly-lit 3-D model. This is
    ///     a dependency property.
    /// </summary>
    public BrushBl Brush { get { return Provider.Property<SpecularMaterial,Brush>(SpecularMaterial.BrushProperty); } set { Brush.Bind = value; } }
    /// <summary>
    ///     Relative contribution, for a material applied as a 2-D brush to a 3-D model,
    ///     of the specular component of the lighting model.
    /// </summary>
    public DoubleBl Power { get { return Provider.Property<SpecularMaterial,double>(SpecularMaterial.SpecularPowerProperty); } set { Power.Bind = value; } }

    public override Lighting ApplyLights(LightGroup Light) {
      return new SpecularMaterialCl() {
        Is2D = false,
        Knob = Knob, Power = Power,
      }.ApplyLights(Light);
    }
  }
  /// <summary>
  ///     Applies a System.Windows.Media.Brush to a 3-D model so that it participates
  ///     in lighting calculations as if the System.Windows.Media.Media3D.Material
  ///     were emitting light equal to the color of the System.Windows.Media.Brush.
  /// </summary>
  public class EmissiveMaterialBl : MaterialBl<EmissiveMaterial, EmissiveMaterialBl> {
    public EmissiveMaterialBl(Expr<EmissiveMaterial> Underlying) : base(Underlying) { }
    public EmissiveMaterialBl() : this(new Constant<EmissiveMaterial>(new EmissiveMaterial())) { }
    public static implicit operator EmissiveMaterialBl(Expr<EmissiveMaterial> target) { return new EmissiveMaterialBl((target)); }
    public static implicit operator EmissiveMaterialBl(EmissiveMaterial target) { return (new Constant<EmissiveMaterial>(target)); }
    /// <summary>
    ///     The color of the light.
    /// </summary>
    public ColorBl Color { get { return Provider.Property<EmissiveMaterial,Color>(EmissiveMaterial.ColorProperty); } set { Color.Bind = value; } }
    /// <summary>
    ///     The brush applied by the System.Windows.Media.Media3D.EmissiveMaterial. The
    ///     default value is null.
    /// </summary>
    public BrushBl Brush { get { return Provider.Property<EmissiveMaterial,Brush>(EmissiveMaterial.BrushProperty); } set { Brush.Bind = value; } }
    public Lighting this[ILight Light] {
      get { throw new NotImplementedException(); }
    }
    public override Lighting ApplyLights(LightGroup Light) {
      throw new NotImplementedException();
    }
  }
  /// <summary>
  ///     Classes that derive from this abstract base class define 3D geometric shapes.
  ///     The System.Windows.Media.Media3D.Geometry3D class of objects can be used
  ///     for hit-testing and rendering 3D graphic data.
  /// </summary>
  public abstract class Geometry3DBl<T, BRAND> : Brand<T, BRAND>, IExtends<BRAND,Geometry3DBl>
    where T : Geometry3D
    where BRAND : Geometry3DBl<T, BRAND> {
    public Geometry3DBl(Expr<T> Underlying) : base(Underlying) { }
    public Geometry3DBl() : base() { }
    public static implicit operator Geometry3DBl(Geometry3DBl<T, BRAND> geom) {
      return Geometry3DBl.UpCast(geom);
    }
  }
  /// <summary>
  ///     Classes that derive from this abstract base class define 3D geometric shapes.
  ///     The System.Windows.Media.Media3D.Geometry3D class of objects can be used
  ///     for hit-testing and rendering 3D graphic data.
  /// </summary>
  public class Geometry3DBl : Geometry3DBl<Geometry3D, Geometry3DBl> {
    public Geometry3DBl(Expr<Geometry3D> Underlying) : base(Underlying) { }
    public Geometry3DBl() : base(new MeshGeometry3D()) { }
    public static implicit operator Geometry3DBl(Expr<Geometry3D> target) { return new Geometry3DBl((target)); }
    public static implicit operator Geometry3DBl(Geometry3D target) { return (new Constant<Geometry3D>(target)); }
  }
  /// <summary>
  ///     Triangle primitive for building a 3-D shape.
  /// </summary>
  public class MeshGeometry3DBl : Geometry3DBl<MeshGeometry3D, MeshGeometry3DBl> {
    public MeshGeometry3DBl(Expr<MeshGeometry3D> Underlying) : base(Underlying) { }
    public MeshGeometry3DBl() : this(new Constant<MeshGeometry3D>(new MeshGeometry3D())) { }
    /// <summary>
    /// Create mesh whose contents are initialized to specified Bling Mesh.
    /// </summary>
    public MeshGeometry3DBl(IMesh3D Mesh) : this() {
      var AsPos = IntBl.Lambda(i => Mesh[i].Position);
      Func<int, Vecs.IVec<double, D3>> AsNormal;
      if (Mesh.HasNormals) AsNormal = IntBl.Lambda(i => Mesh[i].Normal);
      else AsNormal = null;
      Func<int, Vecs.IVec<double, D2>> AsTexture;
      if (Mesh.HasTextures) AsTexture = IntBl.Lambda(i => Mesh[i].Texture);
      else AsTexture = null;
      var Positions0 = Positions.CurrentValue;
      var Normals0 = Normals.CurrentValue;
      var Textures0 = TextureCoordinates.CurrentValue;
      var VertexCount = Mesh.VertexCount.CurrentValue;
      for (int i = 0; i < VertexCount; i++) {
        var pos = AsPos(i);
        Positions0.Add(new Point3D(pos[0], pos[1], pos[2]));
        if (Mesh.HasNormals) {
          var norm = AsNormal(i);
          Normals0.Add(new Vector3D(norm[0], norm[1], norm[2]));
        }
        if (Mesh.HasTextures) {
          var tex = AsTexture(i);
          Textures0.Add(new Point(tex[0] * -1d, tex[1] * -1d));
        }
      }
      if (Mesh.Indices != null) {
        var func = IntBl.Lambda(i => Mesh.Indices(i));
        var pcount = Mesh.PrimitiveCount.CurrentValue;
        for (int i = 0; i < pcount; i++) {
          var tri = func(i);
          for (int j = 0; j < 3; j++)
            TriangleIndices.Add(tri[j]);
        }
        // foreach (var i in Mesh.Indices)
        //   TriangleIndices.Add(i);
      }
    }
    /// <summary>
    /// Create an update function that will update (all at once) the positions and normals (must handle texture coordinates separately).
    /// </summary>
    /// <param name="Mesh">Mesh to compute positions and normals from.</param>
    /// <returns>An action which will update the mesh when called.</returns>
    public Action Refresh(IMesh3D Mesh) {
      var VertexCount = Mesh.VertexCount.CurrentValue;
      Action A = Positions.RefreshAction(i => Mesh[i].Position, VertexCount);
      Action B = Mesh.HasNormals ? Normals.RefreshAction(i => Mesh[i].Normal, VertexCount) : null;
      if (Mesh.HasTextures) {
        var AsTexture = IntBl.Lambda(i => Mesh[i].Texture);
        var Textures0 = TextureCoordinates.CurrentValue;
        for (int i = 0; i < VertexCount; i++) {
          var tex = AsTexture(i);
          Textures0.Add(new Point(tex[0], tex[1]));
        }
      }
      if (Mesh.Indices != null) {
        var PrimitiveCount = Mesh.PrimitiveCount.CurrentValue;
        var TriF = IntBl.Lambda(i => Mesh.Indices(i));
        for (int i = 0; i < PrimitiveCount; i++)
          for (int j = 0; j < 3; j++)
            TriangleIndices.Add(TriF(i)[j]);
        //foreach (var i in Mesh.Indices)
        //  TriangleIndices.Add(i);
      }
      Action ret = () => {
        A();
        if (B != null) B();
      };
      ret();
      return ret;
    }
    public static implicit operator MeshGeometry3DBl(Expr<MeshGeometry3D> target) { return new MeshGeometry3DBl((target)); }
    public static implicit operator MeshGeometry3DBl(MeshGeometry3D target) { return (new Constant<MeshGeometry3D>(target)); }
    /// <summary>
    ///     Gets or sets a collection of vertex positions for a System.Windows.Media.Media3D.MeshGeometry3D.
    ///     This is a dependency property.
    /// </summary>
    public Point3DCollectionBl Positions {
      get { return Provider.Property<MeshGeometry3D,Point3DCollection>(MeshGeometry3D.PositionsProperty); } 
      set { Positions.Bind = value; } 
    }
    /// <summary>
    ///     Gets or sets a collection of texture coordinates for the System.Windows.Media.Media3D.MeshGeometry3D.
    ///     This is a dependency property.
    /// </summary>
    public PointCollectionBl TextureCoordinates {
      get { return Provider.Property<MeshGeometry3D,PointCollection>(MeshGeometry3D.TextureCoordinatesProperty); }
      set { TextureCoordinates.Bind = value; }
    }
    /// <summary>
    ///     Collection that contains the triangle indices of the MeshGeometry3D.
    /// </summary>
    public IntCollectionBl TriangleIndices {
      get { return Provider.Property<MeshGeometry3D,Int32Collection>(MeshGeometry3D.TriangleIndicesProperty); }
      set { TriangleIndices.Bind = value; }
    }
    /// <summary>
    ///     Gets or sets a collection of normal vectors for the System.Windows.Media.Media3D.MeshGeometry3D.
    ///     This is a dependency property.
    /// </summary>
    public Vector3DCollectionBl Normals {
      get { return Provider.Property<MeshGeometry3D,Vector3DCollection>(MeshGeometry3D.NormalsProperty); }
      set { Normals.Bind = value; }
    }
  }
  /// <summary>
  /// Associate a mesh with a number of rows and columns, will set all mesh properties accordingly. 
  /// </summary>
  public class GridMesh {
    private readonly MeshGeometry3DBl Mesh;
    /// <summary>
    /// Create a grid mesh with exisiting mesh geometry and specified column and row counts.
    /// </summary>
    /// <param name="Mesh">Existing mesh to manage.</param>
    /// <param name="ColumnCount">Number of columns in mesh.</param>
    /// <param name="RowCount">Number of rows in Mesh.</param>
    public GridMesh(MeshGeometry3DBl Mesh, int ColumnCount, int RowCount) : base() {
      this.Mesh = Mesh;
      this.ColumnCount = ColumnCount;
      this.RowCount = RowCount;
      Init();
    }
    public static implicit operator Geometry3DBl(GridMesh m) { return m.Mesh; }
    /// <summary>
    /// Create a grid mesh with a new mesh geometry and specified column and row counts.
    /// </summary>
    /// <param name="ColumnCount">Number of columns in mesh.</param>
    /// <param name="RowCount">Number of rows in Mesh.</param>
    public GridMesh(int ColumnCount, int RowCount) : this(new MeshGeometry3DBl(), ColumnCount, RowCount) { }
    /// <summary>
    /// Number of columns in mesh.
    /// </summary>
    public readonly int ColumnCount;
    /// <summary>
    /// Number of rows in mesh.
    /// </summary>
    public readonly int RowCount;
    /// <summary>
    /// Create an action that refreshes mesh geometry based on a 2D array of 3D points values.
    /// </summary>
    /// <param name="values">2D array of 3D points, column and row counts must be the same as the grid mesh.</param>
    /// <returns></returns>
    /*
    public Action Refresh(Bling.Arrays.Array2D<Point3DBl> values) {
      (values.Column.Count == ColumnCount && values.Row.Count == RowCount).Assert();
      var values0 = values.Vertical1D;
      return Mesh.Positions.RefreshAction(i => values0[i], ColumnCount * RowCount);
    }*/
    private void Init() {
      (ColumnCount > 1 && RowCount > 1).Assert();
      DoubleBl dx = 100d / ((double)(ColumnCount - 1));
      DoubleBl dy = 100d / ((double)(RowCount - 1));
      List<Double3Bl> Positions = new List<Double3Bl>();
      List<IntBl> TriangleIndices = new List<IntBl>();
      List<Double2Bl> TextureCoord = new List<Double2Bl>();
      DoubleBl xp = 0;
      double xn = ColumnCount - 1d;
      double yn = RowCount - 1d;
      for (double x = 0; x < ColumnCount; x++) {
        DoubleBl yp = 0;
        for (double y = 0; y < RowCount; y++) {
          Positions.Add(new Double3Bl(xp, yp, 0));
          TextureCoord.Add(new Double2Bl(x / xn, 1.0 - y / yn));
          yp += dy;
        }
        xp += dx;
      }
      for (int x = 0; x < ColumnCount - 1; x++) {
        for (int y = 0; y < RowCount - 1; y++) {
          if ((x + y) % 2 == 1) {
            TriangleIndices.Add(y + (x * RowCount));
            TriangleIndices.Add((y) + ((x + 1) * RowCount));
            TriangleIndices.Add((y + 1) + x * RowCount);

            TriangleIndices.Add(y + ((x + 1) * RowCount));
            TriangleIndices.Add((y + 1) + ((x + 1) * RowCount));
            TriangleIndices.Add((y + 1) + ((x) * RowCount));
          } else {
            TriangleIndices.Add(y + (x * RowCount));
            TriangleIndices.Add((y + 1) + ((x + 1) * RowCount));
            TriangleIndices.Add((y + 1) + x * RowCount);

            TriangleIndices.Add(y + (x * RowCount));
            TriangleIndices.Add(y + ((x + 1) * RowCount));
            TriangleIndices.Add((y + 1) + ((x + 1) * RowCount));
          }
        }
      }
      Mesh.Positions         .InitElements(Positions);
      Mesh.TextureCoordinates.InitElements(TextureCoord);
      Mesh.TriangleIndices   .InitElements(TriangleIndices);
    }
  }

 
  /// <summary>
  ///     Represents an imaginary viewing position and direction in 3-D coordinate
  ///     space that describes how a 3-D model is projected onto a 2-D visual.
  /// </summary>
  public abstract class CameraBl<T, BRAND> : Brand<T, BRAND>, IExtends<BRAND,CameraBl>, ICamera
    where T : Camera
    where BRAND : CameraBl<T, BRAND> {
    public CameraBl(Expr<T> Underlying) : base(Underlying) { }
    public CameraBl() : base() { }

    /// <summary>
    ///     Gets or sets the Transform3D applied to the camera. This is a dependency
    ///     property.
    /// </summary>
    public Transform3DBl Transform {
      get { return Provider.Property<T,Transform3D>(Camera.TransformProperty); }
      set { Transform.Bind = value; }
    }
    public Matrix4Bl Matrix {
      get { return (Project * View).VerifyAffine; }
    }
    public abstract Matrix4Bl View { get; }
    public abstract Matrix4Bl Project { get; }


    public static implicit operator CameraBl(CameraBl<T,BRAND> camera) {
      return CameraBl.UpCast(camera);
    }
  }
  /// <summary>
  ///     Represents an imaginary viewing position and direction in 3-D coordinate
  ///     space that describes how a 3-D model is projected onto a 2-D visual.
  /// </summary>
  public class CameraBl : CameraBl<Camera, CameraBl> {
    public CameraBl(Expr<Camera> Underlying) : base(Underlying) { }
    public CameraBl() : base() { }
    public static implicit operator CameraBl(Expr<Camera> target) { return new CameraBl((target)); }
    public static implicit operator CameraBl(Camera target) { return (new Constant<Camera>(target)); }
    public override Matrix4Bl View {
      get { throw new NotImplementedException(); }
    }
    public override Matrix4Bl Project {
      get { throw new NotImplementedException(); }
    }
  }
  /// <summary>
  ///     An abstract base class for perspective and orthographic projection cameras.
  /// </summary>
  public abstract class ProjectionCameraBl<T, BRAND> : CameraBl<T, BRAND>, IProjectionCamera
    where T : ProjectionCamera
    where BRAND : ProjectionCameraBl<T, BRAND> {
    public ProjectionCameraBl(Expr<T> Underlying) : base(Underlying) { }
    public ProjectionCameraBl() : base() { }

    protected virtual void InitDefaults() {
      //Direction.Look = new Double3Bl(0, 0, 1);
      //Direction.Up = new Double3Bl(0, 1, 0);
      //PlaneDistance.Near = .1d;
      //PlaneDistance.Far = 100d;
      //Position = 0d;
    }

    public class PlaneDistanceSet : IPlaneDistanceSet {
      internal Expr<T> Underlying;
      /// <summary>
      ///     Gets or sets a value that specifies the distance from the camera of the camera's
      ///     far clip plane. This is a dependency property. Default is 100.
      /// </summary>
      public DoubleBl Far {
        get { return Underlying.Property<T,double>(ProjectionCamera.FarPlaneDistanceProperty); }
        set { Far.Bind = value; }
      }
      /// <summary>
      ///     Gets or sets a value that specifies the distance from the camera of the camera's
      ///     near clip plane. This is a dependency property. Default is .1.
      /// </summary>
      public DoubleBl Near {
        get { return Underlying.Property<T,double>(ProjectionCamera.NearPlaneDistanceProperty); }
        set { Near.Bind = value; }
      }
      public IPlaneDistanceSet Bind {
        set { Far = value.Far; Near = value.Near; }
      }
    }
    /// <summary>
    ///     Gets or sets values that specify the distance from the camera of the camera's
    ///     far and near clip planes. This is a dependency property.
    /// </summary>
    public IPlaneDistanceSet PlaneDistance {
      get { return new PlaneDistanceSet() { Underlying = Provider }; }
      set { ((PlaneDistanceSet) PlaneDistance).Bind=(value); }
    }
    public class DirectionSet : IDirectionSet {
      internal Expr<T> Underlying;
      /// <summary>
      ///     Gets or sets a System.Windows.Media.Media3D.Vector3D which defines the direction
      ///     in which the camera is looking in world coordinates. This is a dependency
      ///     property. Default is (0,0,0).
      /// </summary>
      public Double3Bl Look {
        get { return Underlying.Property<T,Vector3D>(ProjectionCamera.LookDirectionProperty).Bl(); }
        set { Look.Bind = value; }
      }
      /// <summary>
      ///     Gets or sets a System.Windows.Media.Media3D.Vector3D which defines the upward
      ///     direction of the camera. This is a dependency property.
      /// </summary>
      public Double3Bl Up {
        get { return Underlying.Property<T,Vector3D>(ProjectionCamera.UpDirectionProperty).Bl(); }
        set { Up.Bind = value; }
      }
      public IDirectionSet Bind {
        set { Look = value.Look; Up = value.Up; }
      }
      static DirectionSet() {
        //Vector3DArity.CheckInit();
      }
    }
    /// <summary>
    ///     Get or set points that define the look and upward
    ///     directions of the camera.
    /// </summary>
    public IDirectionSet Direction {
      get { return new DirectionSet() { Underlying = Provider }; }
      set { ((DirectionSet) Direction).Bind=(value); }
    }
    /// <summary>
    ///     Gets or sets the position of the camera in world coordinates. This is a dependency
    ///     property. Default is (0,0,0).
    /// </summary>
    public Double3Bl Position {
      get { return Provider.Property<T,Point3D>(ProjectionCamera.PositionProperty).Bl(); }
      set { Position.Bind = value; }
    }
    public override Matrix4Bl View {
      get { return Position.LookAt(Direction.Look, Direction.Up); }
    }
    static ProjectionCameraBl() {
      //Point3DArity.CheckInit();
    }
  }
  /// <summary>
  ///     Represents a perspective projection camera.
  /// </summary>
  public class PerspectiveCameraBl : ProjectionCameraBl<PerspectiveCamera, PerspectiveCameraBl>, IPerspectiveCamera {
    public PerspectiveCameraBl(Expr<PerspectiveCamera> Underlying) : base(Underlying) { }
    public PerspectiveCameraBl() : this(new Constant<PerspectiveCamera>(new PerspectiveCamera())) {
      var v = CurrentValue;
      InitDefaults();
    }
    protected override void InitDefaults() {
      base.InitDefaults();
      //FieldOfView = 45d.Bl().Degrees();
    }


    public static implicit operator PerspectiveCameraBl(Expr<PerspectiveCamera> target) { return new PerspectiveCameraBl((target)); }
    public static implicit operator PerspectiveCameraBl(PerspectiveCamera target) { return (new Constant<PerspectiveCamera>(target)); }
    /// <summary>
    ///     The camera's horizontal field of view, in degrees. The default value is 45.
    /// </summary>
    public DegreeBl FieldOfView {
      get { return Provider.Property<PerspectiveCamera,double>(PerspectiveCamera.FieldOfViewProperty).Bl().Degrees(); }
      set { FieldOfView.Bind = value; }
    }

    public override Matrix4Bl Project {
      get { return FieldOfView.Radians.PerspectiveFovLH(1d, PlaneDistance.Near, PlaneDistance.Far); }
    }

    // XXX: doesn't work yet.
    public Double2Bl Size2D {
      set {
        var fovInRadians = FieldOfView.Radians;
        var zValue = value.X / (fovInRadians / 2).Tan / 2;
        Double3Bl negativeZAxis = new Vector3D(0, 0, -1);
        Double3Bl positiveYAxis = new Vector3D(0, 1, 0);
        Double3Bl cameraPosition = new Double3Bl(value.X / 2, value.Y / 2, zValue);
        this.Position = cameraPosition;
        this.Direction.Look = negativeZAxis;
        this.Direction.Up = positiveYAxis;
      }
    }

    /*
    public static explicit operator PerspectiveCameraBl(CameraBl camera) {
      return camera.Underlying.Coerce<PerspectiveCamera>(c => (PerspectiveCamera)c);
    }*/
  }
  /// <summary>
  ///     Represents an orthographic projection camera.
  /// </summary>
  public class OrthographicCameraBl : ProjectionCameraBl<OrthographicCamera, OrthographicCameraBl>, IOrthographicCamera {
    public OrthographicCameraBl(Expr<OrthographicCamera> Underlying) : base(Underlying) { }
    public OrthographicCameraBl() : this(new Constant<OrthographicCamera>(new OrthographicCamera())) { }
    public static implicit operator OrthographicCameraBl(Expr<OrthographicCamera> target) { return new OrthographicCameraBl((target)); }
    public static implicit operator OrthographicCameraBl(OrthographicCamera target) { return (new Constant<OrthographicCamera>(target)); }
    /// <summary>
    ///     Width of the camera's viewing box, in world units.
    /// </summary>
    public DoubleBl Width {
      get { return Provider.Property<OrthographicCamera,double>(OrthographicCamera.WidthProperty); }
      set { Width.Bind = value; }
    }
    public override Matrix4Bl Project {
      get { throw new NotImplementedException(); }
    }
  }

  public abstract partial class Transform3DBl<T, BRAND> : Brand<T, BRAND>, IExtends<BRAND,Transform3DBl> where T : Transform3D where BRAND : Transform3DBl<T,BRAND> {
    public Transform3DBl(Expr<T> v) : base(v) { }
    public Transform3DBl() : base() { }
    public static implicit operator Transform3DBl(Transform3DBl<T, BRAND> value) {
      return Transform3DBl.UpCast(value);
    }
    public Matrix4Bl Value {
      get { return this.Provider.Map<Matrix3D>(transform => transform.Value).Bl(); }
    }

    public Double3Bl Transform(Double3Bl p) {
      var p0 = WPFConversions<double, D3, Point3D>.ToWPF.Instance.Make(p.Provider);
      var p2 = Provider.Map2<Point3D, Point3D>(p0, (t, p1) => t.Transform(p1));
      return WPFConversions<double, D3, Point3D>.ToBling.Instance.Make(p2);
    }
    public Double4Bl Transform(Double4Bl p) {
      var p0 = WPFConversions<double, D4, Point4D>.ToWPF.Instance.Make(p.Provider);
      var p2 = Provider.Map2<Point4D, Point4D>(p0, (t, p1) => t.Transform(p1));
      return WPFConversions<double, D4, Point4D>.ToBling.Instance.Make(p2);
    }
    /// <summary>
    ///     Gets a value that specifies whether the matrix is affine.
    /// </summary>
    public BoolBl IsAffine {
      get { return Map<bool, BoolBl>(t => t.IsAffine); }
    }
    /// <summary>
    /// The inverse of this matrix. 
    /// </summary>
    public Transform3DBl Inverse {
      get {
        Func<T, Transform3D> F = t => (Transform3D)t.Inverse;
        return Map<Transform3D,Transform3DBl>(F);
      }
    }
  }
  public partial class MatrixTransform3DBl : Transform3DBl<MatrixTransform3D, MatrixTransform3DBl> {
    public MatrixTransform3DBl(Expr<MatrixTransform3D> v) : base(v) { }
    public MatrixTransform3DBl() : base(new Constant<MatrixTransform3D>(new MatrixTransform3D())) { }
    public static implicit operator MatrixTransform3DBl(Expr<MatrixTransform3D> v) { return new MatrixTransform3DBl(v); }
    public static implicit operator MatrixTransform3DBl(MatrixTransform3D t) { return new Constant<MatrixTransform3D>(t); }
    public Matrix4Bl Matrix {
      get { return Provider.Property<MatrixTransform3D,Matrix3D>(MatrixTransform3D.MatrixProperty).Bl(); }
      set { this.Matrix.Bind = value;  }
    }

  }
  /// <summary>
  ///     Provides a parent class for all three-dimensional transformations, including
  ///     translation, rotation, and scale transformations.
  /// </summary>
  public partial class Transform3DBl : Transform3DBl<Transform3D, Transform3DBl> {
    public Transform3DBl(Expr<Transform3D> v) : base(v) { }
    public Transform3DBl() : base() { }
    public static implicit operator Transform3DBl(Expr<Transform3D> v) { return new Transform3DBl(v); }
    public static implicit operator Transform3DBl(Transform3D t) { return new Constant<Transform3D>(t); }

    public Double3Bl CenterSize = new Double3Bl(0, 0, 0);

    /// <summary>
    /// Set the center point of the scale/skew/rotate transform .
    /// </summary>
    /// <param name="c">Center point of rotation.</param>
    /// <returns>Itself.</returns>
    public Transform3DBl this[Double3Bl c] {
      get {
        this.CenterSize = c;
        return this;
      }
    }
    private TRANSFORM Transform<TRANSFORM>() where TRANSFORM : Transform3D, new() {
      TRANSFORM transform;
      if (Provider.HasCurrentValue) {
        var current = (Transform3D)this.CurrentValue;
        if (current is TRANSFORM) transform = (TRANSFORM)current;
        else if (current is Transform3DGroup) {
          var group = (Transform3DGroup)current;
          transform = null;
          foreach (var v in group.Children) {
            if (v is TRANSFORM) transform = (TRANSFORM)v;
          }
          if (transform == null) {
            transform = new TRANSFORM();
            group.Children.Add(transform);
          }
        } else if (current == null) {
          transform = new TRANSFORM();
          this.Now = (transform);
        } else {
          var group = new Transform3DGroup();
          group.Children.Add(current);
          transform = new TRANSFORM();
          group.Children.Add(transform);
          this.Now = (group);
        }
      } else {
        throw new NotSupportedException();
      }
      return transform;
    }
    /// <summary>
    ///     Specifies a rotation transformation.
    /// </summary>
    public Rotation3DBl Rotate {
      get {
        var rotate = Transform<RotateTransform3D>();
        var px = rotate.Bl<double>(RotateTransform3D.CenterXProperty).Bl();
        var py = rotate.Bl<double>(RotateTransform3D.CenterYProperty).Bl();
        var pz = rotate.Bl<double>(RotateTransform3D.CenterZProperty).Bl();
        (new Double3Bl(px, py, pz)).Bind = CenterSize;
        Rotation3DBl rotateX = rotate.Bl<Rotation3D>(RotateTransform3D.RotationProperty);
        return rotateX;
      }
      set {
        Rotate.Bind = value;
      }
    }
    /// <summary>
    ///     Scales an object in the three-dimensional x-y-z plane, starting from a defined
    ///     center point. Scale factors are defined in x-, y-, and z- directions from
    ///     this center point.
    /// </summary>
    public Double3Bl Scale {
      get {
        var scale = Transform<ScaleTransform3D>();
        var px = scale.Bl<double>(ScaleTransform3D.CenterXProperty).Bl();
        var py = scale.Bl<double>(ScaleTransform3D.CenterYProperty).Bl();
        var pz = scale.Bl<double>(ScaleTransform3D.CenterZProperty).Bl();
        (new Double3Bl(px, py, pz)).Bind = CenterSize;
        var qx = scale.Bl<double>(ScaleTransform3D.ScaleXProperty).Bl();
        var qy = scale.Bl<double>(ScaleTransform3D.ScaleYProperty).Bl();
        var qz = scale.Bl<double>(ScaleTransform3D.ScaleZProperty).Bl();
        return new Double3Bl(qx, qy, qz);
      }
      set { Scale.Bind = value; }
    }
    /// <summary>
    ///     Translates an object in the three-dimensional x-y-z plane.
    /// </summary>
    public Double3Bl Translate {
      get {
        var translate = Transform<TranslateTransform3D>();
        var qx = translate.Bl<double>(TranslateTransform3D.OffsetXProperty).Bl();
        var qy = translate.Bl<double>(TranslateTransform3D.OffsetYProperty).Bl();
        var qz = translate.Bl<double>(TranslateTransform3D.OffsetZProperty).Bl();
        return new Double3Bl(qx, qy, qz);
      }
      set { Translate.Bind = value; }
    }
  }
  public static partial class WPF3DExtensions {
    static WPF3DExtensions() {
      { // matrix 3D
        var Properties = new System.Reflection.MemberInfo[16];
        for (int i = 0; i < 3; i++) {
          for (int j = 0; j < 4; j++) {
            var id = "M" + (i + 1).ToString() + (j + 1).ToString();
            Properties[i * 4 + j] = typeof(Matrix3D).GetProperty(id);
          }
        }
        Properties[12] = typeof(Matrix3D).GetProperty("OffsetX");
        Properties[13] = typeof(Matrix3D).GetProperty("OffsetY");
        Properties[14] = typeof(Matrix3D).GetProperty("OffsetZ");
        Properties[15] = typeof(Matrix3D).GetProperty("M44");
        Matrix4Bl.SetWPFProperties<Matrix3D>(Properties);
      }
      { // Quaternion
        QuaternionBl.SetWPFProperties<Quaternion>(
          typeof(Quaternion).GetProperty("X"),
          typeof(Quaternion).GetProperty("Y"),
          typeof(Quaternion).GetProperty("Z"),
          typeof(Quaternion).GetProperty("W")
        );
      }
    }
    /*
    public static Expr<Vecs.IVec<double, Dim<D4, D4>>> ToBling(this Expr<Matrix3D> m) {
      return WPFConversions<double, Dim<D4, D4>, Matrix3D>.ToBling.Instance.Make(m);
    }
    public static Expr<Matrix3D> ToWPF(this Expr<Vecs.IVec<double, Dim<D4, D4>>> m) {
      return WPFConversions<double, Dim<D4, D4>, Matrix3D>.ToWPF.Instance.Make(m);
    }*/
    public static Matrix4Bl Bl(this Expr<Matrix3D> m) {
      Matrix4Bl ret = WPFConversions<double, Dim<D4, D4>, Matrix3D>.ToBling.Instance.Make(m);
      ret = ret.Transpose;
      return ret;
    }
    public static Expr<Vecs.IVec<double, D4>> ToBling(this Expr<Quaternion> m) {
      return WPFConversions<double, D4, Quaternion>.ToBling.Instance.Make(m);
    }
    public static Expr<Quaternion> ToWPF(this Expr<Vecs.IVec<double, D4>> m) {
      return WPFConversions<double, D4, Quaternion>.ToWPF.Instance.Make(m);
    }
    public static QuaternionBl Bl(this Expr<Quaternion> m) {
      return m.ToBling();
    }
    public static QuaternionRotation3DBl Bl(this Expr<QuaternionRotation3D> v) { return v; }
    public static QuaternionRotation3DBl Bl(this QuaternionRotation3D v) { return v; }
  }
  /// <summary>
  /// Specifies the 3-D rotation to be used in a transformation.
  /// </summary>
  public abstract partial class Rotation3DBl<T, BRAND> : Brand<T, BRAND>, IExtends<BRAND,Rotation3DBl> where T : Rotation3D where BRAND : Rotation3DBl<T,BRAND> {
    public Rotation3DBl(Expr<T> v) : base(v) { }
    public static implicit operator Rotation3DBl(Rotation3DBl<T, BRAND> value) {
      return Rotation3DBl.UpCast(value);
    }
    static Rotation3DBl() { Rotation3DBl.CheckInit(); }
  }

  /// <summary>
  /// Specifies the 3-D rotation to be used in a transformation.
  /// </summary>
  public class Rotation3DBl : Rotation3DBl<Rotation3D, Rotation3DBl> {
    public Rotation3DBl(Expr<Rotation3D> Underlying) : base(Underlying) { }
    public static implicit operator Rotation3DBl(Expr<Rotation3D> target) { return new Rotation3DBl((target)); }
    public static implicit operator Rotation3DBl(Rotation3D target) { return (new Constant<Rotation3D>(target)); }
    public static readonly Rotation3DBl Identity = Rotation3D.Identity;
    internal static int CheckInit() { Extensions.Trace("INIT: " + ToBrand); return 0; }
  }

  /*
  /// <summary>
  ///     Represents a 3-D rotation of a specified angle about a specified axis.
  /// </summary>
  public class AxisAngleRotation3DBl : Rotation3DBl<AxisAngleRotation3D, AxisAngleRotation3DBl> {
    public AxisAngleRotation3DBl(Expr<AxisAngleRotation3D> v) : base(v) { }

    public AxisAngleRotation3DBl() : this(new Constant<AxisAngleRotation3D>(new AxisAngleRotation3D())) { }
    public static implicit operator AxisAngleRotation3DBl(Expr<AxisAngleRotation3D> v) { return new AxisAngleRotation3DBl(v); }
    public static implicit operator AxisAngleRotation3DBl(AxisAngleRotation3D v) { return new Constant<AxisAngleRotation3D>(v); }

    public static implicit operator AxisAngleRotation3DBl(AxisAngle3DBl angle) {
      return new AxisAngleRotation3DBl() {
        Angle = angle.Angle.AsDegrees, Axis = angle.Axis,
      };
    }
    public static implicit operator AxisAngle3DBl(AxisAngleRotation3DBl angle) {
      return new AxisAngleRotation3DBl() {
        Angle = angle.Angle.AsDegrees,
        Axis = angle.Axis,
      };
    }


    /// <summary>
    ///     Gets or sets the angle of a 3-D rotation, in degrees. This is a dependency
    ///     property.
    /// </summary>
    public DegreeBl Angle { 
      get { return Underlying.Property<double>(AxisAngleRotation3D.AngleProperty).Bl().ToDegrees(); }
      set { Angle.Bind = value; } 
    }
    /// <summary>
    ///     Gets or sets the axis of a 3-D rotation. This is a dependency property.
    /// </summary>
    public Double3Bl Axis {
      get { return Underlying.Property<Vector3D>(AxisAngleRotation3D.AxisProperty); }
      set { Axis.Bind = value; }
    }
  }
  */

  /// <summary>
  /// Represents a rotation transformation defined as a quaternion.
  /// </summary>
  public class QuaternionRotation3DBl : Rotation3DBl<QuaternionRotation3D, QuaternionRotation3DBl> {
    public QuaternionRotation3DBl(Expr<QuaternionRotation3D> v) : base(v) { }
    public QuaternionRotation3DBl() : this(new Constant<QuaternionRotation3D>(new QuaternionRotation3D())) { }
    public static implicit operator QuaternionRotation3DBl(Expr<QuaternionRotation3D> v) { return new QuaternionRotation3DBl(v); }
    public static implicit operator QuaternionRotation3DBl(QuaternionRotation3D v) { return new Constant<QuaternionRotation3D>(v); }
    /// <summary>
    ///  Gets or sets the System.Windows.Media.Media3D.Quaternion that defines the
    ///  destination rotation. This is a dependency property.
    /// </summary>
    public QuaternionBl Quaternion {
      get { return Provider.Property<QuaternionRotation3D,Quaternion>(QuaternionRotation3D.QuaternionProperty).Bl(); }
      set { Quaternion.Bind = value; }
    }
  }
}